package miniworld;

import homeMadeExceptions.Routing_HostNotAvailableException;
import homeMadeExceptions.Routing_HostNotReachableException;

import java.util.HashMap;
import java.util.Vector;

import miniworld.LocalSimulationQueue._call_method;
import simulationEngine.SimulationEngine;
import datatypes.BluetoothDeviceAddress;
import datatypes.Edge;
import datatypes.Host;
import datatypes.LogicalNetworkActionType;
import datatypes.Message;
import datatypes.NetworkAction;
import datatypes.PhysicalNetworkActionType;

public class LogicalNetwork {
	private SimulationEngine engine = null;
	
	// Variables
	private Vector<Host> knownPeers;
	private Vector<Edge> knownEdges;

	
	//Constructor
	public LogicalNetwork(SimulationEngine engine){
		this.engine = engine;
	}
	
	// API for Building logical Network Structure
	// == Network Construction API
	private void addPeer(Host host) {
		if (knownPeers == null) {
			knownPeers = new Vector<Host>();
		}
		knownPeers.add(host);
	}

	private void addEdge(Host host1, Host host2, int weight) {
		String EdgeId = "H" + host1.getID() + "H" + host2.getID() + "W"
				+ weight;
		if (knownEdges == null) {
			knownEdges = new Vector<Edge>();
		}
		knownEdges.add(new Edge(host1, host2, EdgeId, weight));
	}

	private void removePeer(Host host) {
		if (knownPeers != null) {
			for (int i = 0; i < knownPeers.size(); i++) {
				if (knownPeers.get(i).getID() == host.getID()) {
					knownPeers.remove(i);
				}
			}
		}
	}

	private void removeEdge(Edge edge) {
		if (knownEdges != null) {
			boolean removed = false;
			for (int i = 0; i < knownEdges.size() && !removed; i++) {
				if (knownEdges.get(i).getEdgeID().equals(edge.getEdgeID())) {
					knownEdges.remove(i);
					removed = true;
				}
			}
		}
	}

	private void removeEdge(String edge) {
		if (knownEdges != null) {
			boolean removed = false;
			for (int i = 0; i < knownEdges.size() && !removed; i++) {
				if (knownEdges.get(i).getEdgeID().equals(edge)) {
					knownEdges.remove(i);
					removed = true;
				}
			}
		}
	}

	// API vor sending Logical Messages
	// Network USING API
	private void sendMessage(Edge edge) throws Routing_HostNotReachableException, Routing_HostNotAvailableException {
		throw new Routing_HostNotAvailableException(
				"Teil ist noch nicht implementiert");
	}

	private boolean isEdgeAvailable(Edge edge) {
		if (knownEdges != null) {
			for (int i = 0; i < knownEdges.size(); i++) {
				if (knownEdges.get(i).getEdgeID().equals(edge.getEdgeID())) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean isEdgeAvailable(String edgeID) {
		if (knownEdges != null) {
			for (int i = 0; i < knownEdges.size(); i++) {
				if (knownEdges.get(i).getEdgeID().contains(edgeID)) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean isHostAvailable(Host host) {
		if (knownPeers != null) {
			for (int i = 0; i < knownPeers.size(); i++) {
				if (knownPeers.get(i).getID() == host.getID()) {
					return true;
				}
			}
		}
		return false;
	}

	public Vector<Edge> getEdges () {
		return this.knownEdges;
	}

	protected void receiveLogicalMessage(Message msg,
			BluetoothDeviceAddress originHost, BluetoothDeviceAddress targetHost) {
			Vector<Object> parameterlist = new Vector<Object>();
			parameterlist.add(msg);
			parameterlist.add(originHost);
			engine.getHostList().getHost(targetHost).getLocalQueue().add_method_call(_call_method.BLUEZ_RFCOMM_RECV, parameterlist);
			//.getRoutingSocket().rfcomm_recv(msg, originHost);
	}
	
	public void trackLogicalMessage(NetworkAction action) {
//			JOptionPane.showMessageDialog(null, "TODO im " + this.getClass(),
//					"track Logical Message without Standard Algorithm?",
//					JOptionPane.INFORMATION_MESSAGE);
	}

	public void invokeLogicalMessageReceivedAckSignal(NetworkAction action) {
		Vector<Object> parameterlist = new Vector<Object>();
		parameterlist.add(action.getMessage());
		parameterlist.add(action.getTargetHost());
		engine.getHostList().getHost(action.getOriginHost()).getLocalQueue().add_method_call(_call_method.BLUEZ_INVOKEACKSIGNALFORSENDEDMESSAGE, parameterlist);
		//engine.getHostList().getHost(action.getOriginHost()).getRoutingSocket().invokeAckSignalForSendedMessage(action.getMessage(), action.getTargetHost());
	}

	public void sendSignalLogicalMessageLost(NetworkAction action) {
		Vector<Object> parameterlist = new Vector<Object>();
		parameterlist.add(action.getTargetHost());
		parameterlist.add(action.getMessage());
		engine.getHostList().getHost(action.getOriginHost()).getLocalQueue().add_method_call(_call_method.BLUEZ_INVOKE_MESSAGE_LOST, parameterlist);
		//	engine.getHostList().getHost(action.getOriginHost()).getRoutingSocket().invokeMessageLost(action.getTargetHost(), action.getMessage());		
	}
	
	public void sendMessage(BluetoothDeviceAddress source, BluetoothDeviceAddress target, Message msg){
			HashMap <Integer, BluetoothDeviceAddress> tmp_source = new HashMap<Integer,BluetoothDeviceAddress>();
			HashMap <Integer, BluetoothDeviceAddress> tmp_target = new HashMap<Integer,BluetoothDeviceAddress>();
			Integer index = Integer.valueOf(0);
			tmp_source.put(index, source);
			tmp_target.put(index, target);
			this.appendNextAction(new NetworkAction(LogicalNetworkActionType.TRACKING_MESSAGE, tmp_source, tmp_target, PhysicalNetworkActionType.SEND_MESSAGE, msg));
	}
	
	public void sendMessageAlongRoute(HashMap<Integer,BluetoothDeviceAddress> route, Message msg){
		HashMap <Integer, BluetoothDeviceAddress> tmp_source = new HashMap<Integer,BluetoothDeviceAddress>();
		HashMap <Integer, BluetoothDeviceAddress> tmp_target = new HashMap<Integer,BluetoothDeviceAddress>();
		Integer index_src = Integer.valueOf(0);
		Integer index_tgt = Integer.valueOf(1);
		while (route.containsKey(index_src) && route.containsKey(index_tgt)){
			tmp_source.put(index_src, route.get(index_src));
			tmp_target.put(index_tgt, route.get(index_tgt));			
		}
		this.appendNextAction(new NetworkAction(LogicalNetworkActionType.TRACKING_MESSAGE, tmp_source, tmp_target, PhysicalNetworkActionType.SEND_MESSAGE, msg));
	}
	
	private void appendNextAction (NetworkAction nextAction){
		engine.getSimulationEnviroment().getQueue().addActionForNextStep(nextAction);
	}

	public void invokeInquiryScanResult(NetworkAction action) {
		Vector<Host> tmp = engine.getMiniworld().getActiveDevicesInRangeForDevice(Integer.parseInt(action.getOriginHost().getFirstPart()));
		Vector<BluetoothDeviceAddress> answer = new Vector<BluetoothDeviceAddress>();
		for (int i = 0; i < tmp.size(); i++){
			if (!action.getOriginHost().getFirstPart().equals(tmp.get(i).getID()+"")){
				answer.add(tmp.get(i).getBd_Addr());				
			}
		}
		engine.getHostList().getHost(action.getOriginHost()).getRoutingSocket().invokeHciToolAsynchronousInqAnswer(answer);		
	}
	
}
